-
Notifications
You must be signed in to change notification settings - Fork 338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SPIKE] Fix announcement after picking files when using JAWS #5726
Conversation
📋 StatsFile sizes
Modules
View stats and visualisations on the review app Action run for 2ce2c46 |
JavaScript changes to npm packagediff --git a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
index f6220464f..cc295b3cc 100644
--- a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
+++ b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
@@ -779,7 +779,7 @@ class FileUpload extends ConfigurableComponent {
const u = document.createElement("span");
u.className = "govuk-body govuk-file-upload-button__instruction", u.innerText = this.i18n.t("dropInstruction"), l.appendChild(u), s.appendChild(l), s.setAttribute("aria-labelledby", `${i.id} ${a.id} ${s.id}`), s.addEventListener("click", this.onClick.bind(this)), s.addEventListener("dragover", (t => {
t.preventDefault()
- })), this.$root.insertAdjacentElement("afterbegin", s), this.$input.setAttribute("tabindex", "-1"), this.$input.setAttribute("aria-hidden", "true"), this.$button = s, this.$status = r, this.$input.addEventListener("change", this.onChange.bind(this)), this.updateDisabledState(), this.observeDisabledState(), this.$announcements = document.createElement("span"), this.$announcements.classList.add("govuk-file-upload-announcements"), this.$announcements.classList.add("govuk-visually-hidden"), this.$announcements.setAttribute("aria-live", "assertive"), this.$root.insertAdjacentElement("afterend", this.$announcements), this.$button.addEventListener("drop", this.onDrop.bind(this)), document.addEventListener("dragenter", this.updateDropzoneVisibility.bind(this)), document.addEventListener("dragenter", (() => {
+ })), this.$root.insertAdjacentElement("afterbegin", s), this.$input.setAttribute("tabindex", "-1"), this.$input.setAttribute("aria-hidden", "true"), this.$focusMagnet = document.createElement("span"), this.$focusMagnet.setAttribute("tabindex", "-1"), this.$root.appendChild(this.$focusMagnet), this.$button = s, this.$status = r, this.$input.addEventListener("change", this.onChange.bind(this)), this.updateDisabledState(), this.observeDisabledState(), this.$announcements = document.createElement("span"), this.$announcements.classList.add("govuk-file-upload-announcements"), this.$announcements.classList.add("govuk-visually-hidden"), this.$announcements.setAttribute("aria-live", "assertive"), this.$root.insertAdjacentElement("afterend", this.$announcements), this.$button.addEventListener("drop", this.onDrop.bind(this)), document.addEventListener("dragenter", this.updateDropzoneVisibility.bind(this)), document.addEventListener("dragenter", (() => {
this.enteredAnotherElement = !0
})), document.addEventListener("dragleave", (() => {
this.enteredAnotherElement || this.$button.disabled || (this.hideDraggingState(), this.$announcements.innerText = this.i18n.t("leftDropZone")), this.enteredAnotherElement = !1
@@ -812,7 +812,13 @@ class FileUpload extends ConfigurableComponent {
return t
}
onClick() {
- this.$input.click()
+ this.$focusMagnet.focus({
+ preventScroll: !0
+ }), document.addEventListener("focusin", (() => this.$button.focus({
+ preventScroll: !0
+ })), {
+ once: !0
+ }), this.$input.click()
}
observeDisabledState() {
new MutationObserver((t => {
Action run for 2ce2c46 |
Other changes to npm packagediff --git a/packages/govuk-frontend/dist/govuk/all.bundle.js b/packages/govuk-frontend/dist/govuk/all.bundle.js
index b57e1a9a6..d8034bee3 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.js
@@ -1740,6 +1740,9 @@
this.$root.insertAdjacentElement('afterbegin', $button);
this.$input.setAttribute('tabindex', '-1');
this.$input.setAttribute('aria-hidden', 'true');
+ this.$focusMagnet = document.createElement('span');
+ this.$focusMagnet.setAttribute('tabindex', '-1');
+ this.$root.appendChild(this.$focusMagnet);
this.$button = $button;
this.$status = $status;
this.$input.addEventListener('change', this.onChange.bind(this));
@@ -1835,6 +1838,14 @@
return $label;
}
onClick() {
+ this.$focusMagnet.focus({
+ preventScroll: true
+ });
+ document.addEventListener('focusin', () => this.$button.focus({
+ preventScroll: true
+ }), {
+ once: true
+ });
this.$input.click();
}
observeDisabledState() {
diff --git a/packages/govuk-frontend/dist/govuk/all.bundle.mjs b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
index bd59d9bb4..803a86faf 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
@@ -1734,6 +1734,9 @@ class FileUpload extends ConfigurableComponent {
this.$root.insertAdjacentElement('afterbegin', $button);
this.$input.setAttribute('tabindex', '-1');
this.$input.setAttribute('aria-hidden', 'true');
+ this.$focusMagnet = document.createElement('span');
+ this.$focusMagnet.setAttribute('tabindex', '-1');
+ this.$root.appendChild(this.$focusMagnet);
this.$button = $button;
this.$status = $status;
this.$input.addEventListener('change', this.onChange.bind(this));
@@ -1829,6 +1832,14 @@ class FileUpload extends ConfigurableComponent {
return $label;
}
onClick() {
+ this.$focusMagnet.focus({
+ preventScroll: true
+ });
+ document.addEventListener('focusin', () => this.$button.focus({
+ preventScroll: true
+ }), {
+ once: true
+ });
this.$input.click();
}
observeDisabledState() {
diff --git a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js
index 6c37a8243..34cbe048d 100644
--- a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js
@@ -567,6 +567,9 @@
this.$root.insertAdjacentElement('afterbegin', $button);
this.$input.setAttribute('tabindex', '-1');
this.$input.setAttribute('aria-hidden', 'true');
+ this.$focusMagnet = document.createElement('span');
+ this.$focusMagnet.setAttribute('tabindex', '-1');
+ this.$root.appendChild(this.$focusMagnet);
this.$button = $button;
this.$status = $status;
this.$input.addEventListener('change', this.onChange.bind(this));
@@ -662,6 +665,14 @@
return $label;
}
onClick() {
+ this.$focusMagnet.focus({
+ preventScroll: true
+ });
+ document.addEventListener('focusin', () => this.$button.focus({
+ preventScroll: true
+ }), {
+ once: true
+ });
this.$input.click();
}
observeDisabledState() {
diff --git a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs
index 307cbbd67..4d8618bc9 100644
--- a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs
@@ -561,6 +561,9 @@ class FileUpload extends ConfigurableComponent {
this.$root.insertAdjacentElement('afterbegin', $button);
this.$input.setAttribute('tabindex', '-1');
this.$input.setAttribute('aria-hidden', 'true');
+ this.$focusMagnet = document.createElement('span');
+ this.$focusMagnet.setAttribute('tabindex', '-1');
+ this.$root.appendChild(this.$focusMagnet);
this.$button = $button;
this.$status = $status;
this.$input.addEventListener('change', this.onChange.bind(this));
@@ -656,6 +659,14 @@ class FileUpload extends ConfigurableComponent {
return $label;
}
onClick() {
+ this.$focusMagnet.focus({
+ preventScroll: true
+ });
+ document.addEventListener('focusin', () => this.$button.focus({
+ preventScroll: true
+ }), {
+ once: true
+ });
this.$input.click();
}
observeDisabledState() {
diff --git a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs
index 482b1798c..33be5c4d1 100644
--- a/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs
@@ -82,6 +82,9 @@ class FileUpload extends ConfigurableComponent {
this.$root.insertAdjacentElement('afterbegin', $button);
this.$input.setAttribute('tabindex', '-1');
this.$input.setAttribute('aria-hidden', 'true');
+ this.$focusMagnet = document.createElement('span');
+ this.$focusMagnet.setAttribute('tabindex', '-1');
+ this.$root.appendChild(this.$focusMagnet);
this.$button = $button;
this.$status = $status;
this.$input.addEventListener('change', this.onChange.bind(this));
@@ -177,6 +180,14 @@ class FileUpload extends ConfigurableComponent {
return $label;
}
onClick() {
+ this.$focusMagnet.focus({
+ preventScroll: true
+ });
+ document.addEventListener('focusin', () => this.$button.focus({
+ preventScroll: true
+ }), {
+ once: true
+ });
this.$input.click();
}
observeDisabledState() {
Action run for 2ce2c46 |
After selecting a file, JAWS announces the accessible name of the button as it was before selecting. Sometimes this is the last announcement, which can make it confusing for users as the last thing they'll hear is the wrong state (potentially 'No file chosen' if it's the first time they use the component). Moving the focus to another element than the button before picking and explicitely focusing the button once back seems to consistently let JAWS announce the correct state of the component.
baa82f7
to
71cb699
Compare
This'll let us not forget that we have a fix for JAWS in place. Testing requires us to trigger our own `focusin` event as Puppeteer does not seem to support automatic focus/blur events: puppeteer/puppeteer#1462
I've just done another round of screen reader testing and updated the testing spreadsheet accordingly.
I couldn't replicate what @patrickpatrickpatrick reported. But it's very possible I just didn't chose the files quickly enough. |
I have tested what's announced when selecting a file in this against #5729 and #5730. Out of those three #5729 behaves the best. As expected, JAWS with Firefox behaves nearly the same except that it also adds the file name at the end. While that is a bit confusing because it previously said (wrongly) "no file chosen" or the file name of the previously selected file, I think this is sufficient and probably the best we can do. I was also finally be able to test in the latest iOS version (18.3.1). |
Closing in favour of #5729, which provides a better experience. |
After selecting a file, JAWS announces the accessible name of the button as it was before selecting. Sometimes this is the last announcement, which can make it confusing for users as the last thing they'll hear is the wrong state (potentially 'No file chosen' if it's the first time they use the component).
This PR adds an empty
<span>
to the component. Before the file picker opens, the focus is moved to that element (effectively with no accessible name). This doesn't have any adverse effect as the focus moves straight away to the file picker.Upon returning on the page, we explicitely set the focus to the button so users are in the correct position on the page.
This allows JAWS + Chrome to announce the correct state for the button 🥳 Tests in NVDA + Chrome and VoiceOver + Safari sounded OK as well.
Thoughts
Button's labelling
Testing other ways to label the
<button>
(using the button's content oraria-label
) showed that this was not an issue with our use ofaria-labelledby
to set the accessible name.JAWS Announcement
JAWS is very eager to announce a ton of information when coming back to the page from the file picker:
<STATUS_BEFORE_PICKING>
, Chose file or drop file button, to activate press Enter<SELECTED_FILE_NAME>
, Chose file or drop file buttonSometimes 5 gets announced at the start of the list, making it very confusing to users as the last thing they'll hear is the status of the file picker before they clicked. Even when announced at the end of the list, the name of the button is announced twice in a row, with just a slight difference in the middle 😔
Alternative solutions considered
Moving the focus explicitly, immediately
First idea was to move the focus explicitly to the button, immediately when coming back into the document.
Updating
onClick
to:No change to the announcement unfortunately.
Moving the focus explicitly, delayed
Same as before, but with a 2 second delay
Updating
onClick
to:No change to the announcement either.
Announcement of the status, immediately
When the status changes, make an announcement of the new status. This can be easily added by setting
aria-live="polite"
to the status element.This seems to reliably add an announcement of the file name after step 4. Meaning that if 5. happens as the first thing, the last thing people hear is the name of the selected file or 'N selected files'. There's still a double announcement of the button.
Announcement of the status, debounced
Given the immediate announcement of the status already happens after JAWS re-announces the window, page, landmark you're in, I don't think it'll make a difference to delay the announcement to be debounced by a couple of seconds.